#ifndef HTTPCONNECTION_H
#define HTTPCONNECTION_H

#include <sys/epoll.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/mman.h> //内存映射
#include <stdarg.h>
#include <errno.h>
#include <sys/uio.h>
#include "locker.h"
#include <iostream>
#include <string.h>
#include <sstream>
#include <string>
#include <regex>
#include <dirent.h>
#include <ctype.h>
#include <fstream>
#include "sql_connection_pool.h"
#include "threadpool.h"

using namespace std;


// 添加文件描述符到epoll中
void addfd(int epollfd, int fd, bool one_short);
// 从epoll中删除文件描述符
void removefd(int epollfd, int fd);
// 修改文件描述符
void modfd(int epollfd, int fd, int ev, bool is_epollET=true);

class http_conn
{
public:
    static const int FILENAME_LEN = 200; // 文件名的最大长度
    static const int DIRHTML_LEN = 4096;  // 显示目录信息的html最大长度
    static const int READ_BUFFER_SIZE = 2048;   // 读缓冲区的大小
    static const int WRITE_BUFFER_SIZE = 4096;  // 写缓冲区的大小

    static int m_epollfd; //所有的socket上的事件都被注册到同一个epoll对象中，所以设为静态
    static int m_user_count; // 统计用户数量
    static connection_pool* m_p_conn_pool;
    static shared_ptr<threadpool<http_conn>> m_logic_thread_pool;

    // HTTP请求方法，但我们只支持GET
    enum METHOD {GET = 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT};
    // 解析客户端请求时，主状态机的状态 
    // CHECK_STATE_REQUESTLINE:当前正在分析请求行
    // CHECK_STATE_HEADER:当前正在分析头部字段
    // CHECK_STATE_CONTENT:当前正在解析请求体
    enum CHECK_STATE {CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT};
    // 从状态机的三种可能状态，即行的读取状态
    // 0.读取到一个完整的行 1.行出错(如语法错误) 2.行数据尚不完整(还没有遇到/r/n)
    enum LINE_STATUS {LINE_OK = 0, LONE_BAD, LINE_OPEN};
    // 服务器处理HTTP请求的可能结果，报文解析的结果
    // NO_REQUEST:请求不完整，需要继续读取客户数据
    // GET_REQUEST:获得了一个完整的客户请求
    // BAD_REQUEST:客户请求语法错误
    // NO_RESOURCE:服务器没有资源
    // FORBIDDEN_REQUEST:客户对资源没有足够的访问权限
    // FILE_REQUEST:文件请求，获取文件成功
    // DIR_REQUEST:目录请求，获取目录成功
    // INTERNAL_ERROR:服务器内部出错
    // CLOSED_CONNECTION:客户端已经关闭连接
    enum HTTP_CODE {NO_REQUEST, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST, FILE_REQUEST, DIR_REQUEST, UPLOAD_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION};
    // 请求的数据库内容
    // CATEGORY:类别目录
    // POST_DIRECTORY:某个分类中帖子目录
    // POST_CONTANT:帖子内容
    enum SQL_CONTTANT {CATEGORY, POST_DIRECTORY, POST_CONTANT};
    // 正在解析的帖子内容
    // POST_ST:首次进入POST消息体
    // POST_TITLE:解析题目
    // POST_CLASSID:解析类别
    // POST_TEXT:解析正文
    // POST_FILE:解析文件
    // enum POST_STATE {POST_ST, POST_TITLE, POST_CLASSID, POST_TEXT, POST_FILE};

    http_conn(){}
    ~http_conn(){}

    void process1();
    void process2(); // 处理HTTP请求的入口函数
    void init(int sockfd, const sockaddr_in& addr); // 初始化新接收的连接
    void close_conn(); // 关闭连接
    bool read();    // 非阻塞地读
    bool write();   // 非阻塞地写

    bool is_need_read;

private:
    void state_init(); // 初始化当前参数状态
    
    bool save_file();
    bool save_comment();

    HTTP_CODE process_read(); // 解析HTTP请求
    bool process_write(HTTP_CODE ret); // 填充HTTP应答

    // 下面这组函数被process_read调用以分析HTTP请求
    HTTP_CODE parse_request_line(string& text); // 解析请求首行
    HTTP_CODE parse_headers(stringstream ss); // 解析请求头
    HTTP_CODE parse_content(); // 解析请求体
    string analyMultipartContent(string& text, int boundaryIdx1, int boundaryIdx2);

    LINE_STATUS parse_line(string& text); // 解析一行
    // char* get_line() { return m_read_buf + m_start_line; } // 获取一行数据
    HTTP_CODE do_request();

    // 下面这组函数被process_read调用以填充HTTP应答
    void unmap();
    bool add_response(const char* format, ...);
    bool add_content(const string& content);
    bool add_status_line(int status, const string& title);
    bool add_headers(int content_length);
    bool add_content_type();
    bool add_content_length(int content_length);
    bool add_linger();
    bool add_blank_line();

    bool signin();
    bool registered();
    HTTP_CODE make_dir_html(SQL_CONTTANT conttant);
    bool get_sql_result(const char* query, bool is_query, vector<vector<string>>* p_result=nullptr);
    
    int hexit(char c);
    void encode_str(char* to, int tosize, const char* from);
    void decode_str(char* to, const char* from);


    int m_sockfd=-1;  // 该HTTP连接的socket文件描述符
    sockaddr_in m_address;  // 对方的socket地址
    char m_read_buf[READ_BUFFER_SIZE];  // 读缓冲区
    int m_read_idx=0; // 读缓冲区中已经读入客户端数据的最后一个字节的下一个位置,即读入数据的个数
    char m_write_buf[WRITE_BUFFER_SIZE]; // 写缓冲区
    int m_write_idx; // 写缓冲区中待发送的字节数
    bool is_readover; // 缓冲区中的数据已被读完
    bool is_loadfile; // 正在下载文件，之前已将信息插入数据库

    CHECK_STATE m_check_state; // 主状态机当前所处的状态
    // POST_STATE m_post_state; // 正在解析的帖子内容
    int m_checked_index; // 当前正在分析的字符在读缓冲区的位置
    // int m_start_line; // 当前正在解析的行的起始位置

    //客户请求的目标文件的完整路径，其内容等于doc_root+m_url,doc_root是文件根目录
    char m_real_file[FILENAME_MAX]; 
    string m_url;        // 请求目标文件的文件名
    string m_version;    // 协议版本，只支持HTTP1.1 ......................
    METHOD m_method;    // 请求方法
    string m_host;       // 主机名
    string m_content_type; // 请求体类型
    string m_boundary;  // multipart/form-data数据的分界线
    bool m_linger;      // HTTP请求是否保持连接
    int m_content_length; // 请求消息体长度
    string m_content_text;
    string m_user_name;
    string m_user_id;
    string m_postfile; // 发送帖子中的文件名

    char* m_file_address; // 客户请求的目标文件被mmap到内存中的起始位置
    // 目标文件的状态。通过它我们可以判断文件是否存在、是否为目录、是否可读，并获取文件大小等信息
    struct stat m_file_stat;
    // 我们采用writev来执行写操作，所以定义下面两个成员，其中m_iv_count表示被写内存块的数量
    struct iovec m_iv[2];
    int m_iv_count;

    char m_dir_html[DIRHTML_LEN]; // 发送给用户的显示目录信息的html
};



#endif